#ifndef _ARCHIVE_H_
#define _ARCHIVE_H_

#include "num/named_set.h"
#include "vfs.h"
#include "util/file_io.h"		    

namespace vfs
{

/***
 * @brief: Archive class for building and loading pak files using ZLIB
 */
class	Archive 
{
public:
	/**
	 * @brief:	Constructor
	 */
	Archive();
	/**
	 * @brief:	Destructor
	 */
	~Archive();
	/**
	 * @brief:		Load Archive from Disk. Will aggregate with data that already exists
	 * @param:		File	File to load from
	 * @returns:	true if succeeded, false otherwise
	 */
	bool		LoadArchive(const std::string& File);
	/**
	 * @brief:		Save Archive to Disk
	 * @param:		File	File to save
	 * @returns:	true if succeeded, false otherwise
	 */
	bool		SaveArchive(const std::string& File);
	/**
	 * @brief:		Check if a file exists
	 * @param:		File	File to look for (including paths)
	 * @returns:	true if exists, false otherwise
	 */
	bool		FileExists(const std::string& File);
	/**
	 * @brief:		Retrieve list of files fitting the input criteria
	 * @param:		List	List names to populate
	 * @param:		Path	Path to search in
	 * @param:		Ext		Extension to search for (or empty for all)
	 * @returns:	number of files found
	 */
	int			BuildFileList(std::vector<std::string>& List, const std::string& Path, const std::string& Ext = "");

	/**
	 * @brief:				Retrieve file from archive
	 * @param:		File	File to look for (including paths)
	 * @param:		Block	Block to load into
	 * @returns:			true if exists, false otherwise
	 */
	bool		GetFile(const std::string& File, LoadedBlock& Block);
	bool		GetFile(unsigned int FileID, LoadedBlock& Block);

	/**
	 * @brief:		Add file from archive if a file with that name doesn't already exist
	 * @param:		File to add (including path)
	 * @returns:	true if exists, false otherwise
	 */
	bool		AddFile(const std::string& File);

	/**
	 * @brief:		Cache all files from disk at once. Usefull for dense archives that have 100% coverage to used set
	 * @returns:	true if okay.
	 */
	bool		CacheAll();

	/**
	 * @brief:		Unpack the library back into files
	 * @param:		basic directory
	 * @returns:	True if okay
	 */
	bool		Unpack(const std::string& Dest);
	

	/**
	 * @brief:		Create a new archive (used by script)
	 * @returns:	new archive
	 */
	static Archive*		CreateArchive();

	/**
	 * @brief:		Register functions to script
	 */
	static void			Register(LuaPlus::LuaState* _pScript);

	/**
	 * @brief:		Register functions to script
	 */
	
	static unsigned long FindCompressBound(unsigned long	Size);
private:
	static const int	FILE_LENGTH		=	256;
	static const int	MAGIC_NUMBER	=	'SARC';	// sleipner archive
	static const int	VERSION_NUMBER	=	0x01;	// first version
	enum		RES_FLAGS
	{
		RES_COMPRESSED		= 0x01 << 0,
		RES_UNCOMPRESSED	= 0x01 << 1,
		RES_FORCE			= 0xFF
	};
	struct		FileHeader
	{
		unsigned int		MagicNum;
		unsigned int		VersionNum;
		unsigned int		FileCount;
		
		// table of contents index
		unsigned long		TOCIndex;
		unsigned long		TOCSizeCompressed;
		unsigned long		TOCSize;
		// actual data index
		unsigned long		DataIndex;
		unsigned long		DataSizeCompressed;
		unsigned long		DataSize;
	};
	/***
	 * @brief:	Single file information
	 */
	struct		FileEntry
	{
		FileEntry() :
			CompressedIndex(0),
			CompressedSize(0),
			pData(NULL),
			UncompressedSize(0),
			Flags(0)
		{

		};
		~FileEntry();
		char				FileName[FILE_LENGTH];		//! Relative pathname
		unsigned long		CompressedIndex;		//! Offset from start of file
		unsigned long		CompressedSize;			//!	Size on disk
		void*				pData;					//! location in memory
		unsigned long		UncompressedSize;		//! Size in memory
		unsigned int		Flags;					//! Flags
	};

	/**
	 * @brief:		Force a file to cache
	 * @param:		pEntry		Entry to cache if not already loaded
	 * @returns:	true if okay.
	 */
	bool		CacheEntry(FileEntry* pEntry);
	/**
	 * @brief:		Callback used to drive caching from Set
	 */
	static void	CacheEntryCB(FileEntry* pEntry, void* pData);
	/**
	 * @brief:		Callback used to drive output to file
	 */
	static void	ExportEntryCB(FileEntry* pEntry, void* pData);
	/**
	 * @brief:		Callback collect all the file entries
	 */
	static void	CollectEntriesCB(FileEntry* pEntry, void* pData);



	/// map from files to memory
	num::NamedSet<FileEntry*>			m_FileSet;
	/// Uncompress all at load?
	bool								m_AllCached;
	/// file loaded from or saved to (if any) 
	std::string							m_FileName;
	util::FileIO						m_File;
};

} // namespace vfs
#endif // _ARCHIVE_H_
